Skip to content

获取真实 ip

更新时间: 10/30/2024字数: 0 字

公开版:https://www.hackjie.com/tracking

自己实现版本:https://codepen.io/cc-Jimmy/pen/RwzzEpL?editors=1111

测试反馈:

  1. 代理软件使用 Tun 模式,全部失效
  2. iPhone 全部浏览器失效
  3. 代理软件不使用 Tun 模式,能正常拿到真实 ip 地址

webrtc 技术分析:

WebRTC是一个开源项目,通过简单的 API 为 Web 浏览器和移动的应用程序提供实时通信功能。

WebRTC支持浏览器之间的音频、视频和数据流,允许开发人员构建视频聊天、文件传输和屏幕共享等应用程序,而无需依赖第三方插件或扩展。

WebRTC由几个关键组件组成,这些组件共同提供实时通信:

  • getUserMedia:允许访问用户的网络摄像头和麦克风。
  • RTCPeerConnection:管理建立和维护对等点之间连接的整个过程。
  • RTCDataChannel:支持对等点之间的双向数据传输,允许低延迟通信。
  • WebRTC依赖于几种协议来建立和维护对等体之间的连接:
  • ICE(Interactive Connectivity Establishment):通过 NAT(Network Address Translators)和防火墙连接对等体的框架,ICE 使用 STUN 和 TURN 服务器来发现和中继网络信息。
  • STUN(Session Traffic Utilities for NAT):一种允许客户端发现其公有 IP 地址和所处 NAT 类型的协议。
  • TURN(使用 NAT 周围的中继进行传输):为由于 NAT 限制或防火墙而无法建立直接连接的对等体提供中继服务器的协议。

需要注意的是,使用WebRTC获取用户 IP 地址可能会引发隐私问题。在收集任何信息(包括 IP 地址)之前,必须通知您的用户并征得他们的同意。

要使用WebRTC获取用户的 IP 地址,您可以利用 ICE 流程,该流程在对等点之间交换网络信息。在 ICE 过程中,浏览器收集本地 IP 地址并生成包含此信息的 ICE 候选。

  1. 创建 RTCPeerConnection

    使用 ICE 服务器创建一个RTCPeerConnection

ts
const iceServers = [
  {
    urls: 'stun:stun.l.google.com:19302'
  }
]

const peerConnection = new RTCPeerConnection({ iceServers })

这个连接对象表示两个对等体之间的连接,负责管理数据交换。

stun:stun.l.google.com:19302”是由 Google 提供的公共 STUN 服务器,它是一个高度可靠的免费公共 STUN 服务器。如果需要,您可以选择使用自己的 STUN 服务器。

  1. 创建数据通道

    对于某些浏览器(如 Chrome),创建数据通道是触发 ICE 进程所必需的。使用createDataChannel方法创建数据通道

    ts
    peerConnection.createDataChannel('')
  2. 监听onicecandidate事件以在生成 ICE 候选时收集 IP 地址

    ts
    peerConnection.onicecandidate = (event) => {
      console.log('RTC Peer Connection Ice Event:', event)
    }
  3. 触发 ICE 调用createOffersetLocalDescription方法触发 ICE 进程

    ts
    peerConnection
      .createOffer()
      .then((offer) => peerConnection.setLocalDescription(offer))
      .catch((error) => console.error('Error creating offer:', error))
  4. 提取 IP 地址

    每个 ICE 候选包含关于潜在网络路径的信息,包括 IP 地址和端口。

    所以在onicecandidate中,创建一个正则表达式来提取 IP 地址

    ts
    const ipv4Regex =
      /\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/
    
    const ipv6Regex = /\b(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}\b/i
    
    const ipSet = new Set()
    
    const onicecandidate = (ice: RTCPeerConnectionIceEvent) => {
      const candidate = ice?.candidate?.candidate
    
      if (candidate) {
        for (const regex of [ipv4Regex, ipv6Regex]) {
          const [ip] = candidate.match(regex) ?? []
          if (ip) {
            ipSet.add(ip)
          }
        }
      }
    }

完整版代码

ts
const ipv4Regex =
  /\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/

const ipv6Regex = /\b(?:[A-F0-9]{1,4}:){7}[A-F0-9]{1,4}\b/i

// prettier-ignore
// @ts-expect-error
globalThis.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection

const publicIPs = (timeout = 1000) => {
  const ipSet = new Set()

  const onicecandidate = (ice: RTCPeerConnectionIceEvent) => {
    const candidate = ice?.candidate?.candidate

    if (candidate) {
      for (const regex of [ipv4Regex, ipv6Regex]) {
        const [ip] = candidate.match(regex) ?? []
        if (ip) {
          ipSet.add(ip)
        }
      }
    }
  }

  return new Promise((resolve, reject) => {
    const conn = new globalThis.RTCPeerConnection({
      iceServers: [
        {
          urls: 'stun:stun.l.google.com:19302'
        }
      ]
    })
    conn.addEventListener('icecandidate', onicecandidate)
    conn.createDataChannel('')
    conn.createOffer().then((offer) => conn.setLocalDescription(offer), reject)

    setTimeout(() => {
      try {
        conn.removeEventListener('icecandidate', onicecandidate)
        conn.close()
      } catch {
        // ignore
      }

      resolve([...ipSet])
    }, timeout)
  })
}

publicIPs().then(() => {})

Released under the MIT License.